﻿using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;

namespace winS.UnityEditor.UIElement
{
    /// <summary>
    /// Text field with search function.
    /// </summary>
    public class SearchField : TextField
    {
        /// <summary>
        /// 当前值是否无效.
        /// 如果 <see cref="searchList"/> 未包含当前值, 则返回 true
        /// </summary>
        public bool invalidValue { get; private set; }

        public IList<string> searchList { get => _searchList; set => SetSearchList(value); }
        public SearchContent searchContent => _searchContent ??= CreateSearchContent();

        private readonly Icon arrowIcon;

        private bool focusFlag;
        private Icon warningIcon;
        private IList<string> _searchList;
        private SearchContent _searchContent;

        internal SearchField(ElementFactory elementFactory) : base(elementFactory)
        {
            focusable = true;
            textInput.Add(arrowIcon = EditorElementUtility.elementFactory.CreateIcon(PackageAssets.Icons.arrowDown, 12f, elementFactory.softWhiteColor));
            arrowIcon.style.flexShrink = 0f;
            arrowIcon.style.marginRight = 2f;
            arrowIcon.style.alignSelf = Align.Center;
            arrowIcon.focusable = false;
            arrowIcon.SetEnabled(false);
            EditorElementUtility.RegisterClickEvent(arrowIcon, ShowAllSearchContent);

            textElement.RegisterCallback<BlurEvent>(BlurCallback);
            textElement.RegisterCallback<KeyDownEvent>(KeyDownCallback);
            textField.RegisterValueChangedCallback(ValueChangedCallback);
        }

        public override void SetValueWithoutNotify(string value)
        {
            base.SetValueWithoutNotify(value);
            CheckValueValidity();
        }

        protected override void SetValue(string value)
        {
            base.SetValue(value);
            CheckValueValidity();
        }

        private void CheckValueValidity()
        {
            int index = searchList != null ? searchList.Count == 0 ? 0 : searchList.IndexOf(value) : -1;
            if (index != -1)
            {
                if (invalidValue)
                {
                    textField.ElementAt(0).Remove(warningIcon);
                    invalidValue = false;
                }
                return;
            }
            invalidValue = true;
            if (warningIcon == null)
            {
                warningIcon = EditorElementUtility.elementFactory.CreateIcon(PackageAssets.Icons.warnning, 12f, new Color32(183, 46, 46, 255));
                warningIcon.style.marginRight = 2f;
                warningIcon.style.flexShrink = 0f;
            }
            textField.ElementAt(0).Insert(1, warningIcon);
        }

        private void SetSearchList(IList<string> newSearchList)
        {
            _searchList = newSearchList;
            if (_searchContent != null) searchContent.SetChoiceList(newSearchList);
            arrowIcon.SetEnabled(newSearchList != null && newSearchList.Count != 0);
            CheckValueValidity();
        }
        private void UpdateSearchContent(string matchValue)
        {
            if (!searchContent.UpdateData(matchValue))
            {
                searchContent.Close();
                return;
            }
            if (!searchContent.opening)
            {
                searchContent.ShowInBackground(textElement);
                searchContent.selectIndex = 0;
            }
            else schedule.Execute(() => searchContent.RecalculateAll()).StartingIn(10);
        }
        private void ShowAllSearchContent()
        {
            searchContent.UpdateAll();
            if (!searchContent.opening)
            {
                searchContent.ShowInBackground(textElement);
                searchContent.selectIndex = 0;
            }
            else schedule.Execute(() => searchContent.RecalculateAll()).StartingIn(10);
        }
        private SearchContent CreateSearchContent()
        {
            SearchContent searchContent = new SearchContent(_searchList, newValue =>
            {
                if (value == newValue) return;
                value = newValue;
            });
            searchContent.onClose += () => focusFlag = false;
            searchContent.onScrollBarDragStart += () => focusFlag = true;
            return searchContent;
        }

        private void BlurCallback(BlurEvent blurEvent)
        {
            schedule.Execute(() =>
            {
                if (focusFlag) return;
                searchContent.Close();
            }).StartingIn(100);
        }
        private void KeyDownCallback(KeyDownEvent keyDownEvent)
        {
            if (!searchContent.opening) return;
            switch (keyDownEvent.keyCode)
            {
                case KeyCode.Return:
                    value = searchContent.currentValue;
                    searchContent.Close();
                    break;
                case KeyCode.UpArrow: searchContent.Up(); break;
                case KeyCode.DownArrow: searchContent.Down(); break;
                default: return;
            }
            keyDownEvent.StopImmediatePropagation();
        }
        private void ValueChangedCallback(ChangeEvent<string> changeEvent)
        {
            string newValue = changeEvent.newValue;
            SetValueWithoutNotify(newValue);
            UpdateSearchContent(newValue);
        }

        public class SearchContent : DropdownMenu
        {
            public string currentValue => selectIndex < 0 ? data.Count > 0 ? data[0] : string.Empty : data[selectIndex];

            private IList<string> choiceList;

            private readonly List<string> matchList = new List<string>();

            public SearchContent(IList<string> choiceList, Action<string> onSelectedCallback) : base(choiceList, onSelectedCallback)
            {
                enableAdaptive = false;
                this.choiceList = choiceList;
                EnableKeyboardMove();
            }

            public void SetChoiceList(IList<string> choiceList)
            {
                this.choiceList = choiceList;
            }

            public void UpdateAll()
            {
                SetData(choiceList);
            }
            public bool UpdateData(string newValue)
            {
                if (choiceList == null) return false;
                if (newValue == string.Empty) return false;
                matchList.Clear();
                foreach (var choice in choiceList)
                {
                    if (!choice.Contains(newValue, StringComparison.OrdinalIgnoreCase)) continue;//忽略大小写
                    matchList.Add(choice);
                }
                SetData(matchList);
                return matchList.Count > 0;
            }
        }
    }
}